#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include "list/string_list.h"
#include "gui/gui.h"
#include "utils_string.h"
#include "utils.h"

char *strnchr(const char *str, size_t n, int c)
{
    for (size_t i = 0; i < n; ++i)
    {
        if (str[i] == c)
        {
            return (char *)&(str[i]);
        }
    }
    return NULL;
}

void StringTrim(char *str)
{
    int len = strlen(str);
    int i;

    for (i = len - 1; i >= 0; i--)
    {
        if (str[i] == ' ' || str[i] == '\t')
            str[i] = 0;
        else
            break;
    }
}

int StringToDecimal(const char *str)
{
    return strtol(str, NULL, 0);
}

unsigned int StringToHexdecimal(const char *str)
{
    return strtoul(str, NULL, 16);
}

int StringToBoolean(const char *str)
{
    if (strcasecmp(str, "false") == 0 ||
        strcasecmp(str, "off") == 0 ||
        strcasecmp(str, "no") == 0)
        return 0;

    if (strcasecmp(str, "true") == 0 ||
        strcasecmp(str, "on") == 0 ||
        strcasecmp(str, "yes") == 0)
        return 1;

    return -1;
}

int StringGetLine(const char *buffer, int size, char **line)
{
    int n = 0;
    int i = 0;
    uint8_t *p = (uint8_t *)buffer;

    for (i = 0; i < size; i++)
    {
        if (p[i] < 0x20 && p[i] != '\t')
        {
            i++;
            break;
        }
        n++;
    }

    if (line && n > 0)
    {
        *line = malloc(n + 1);
        if (*line)
        {
            strncpy(*line, buffer, n);
            (*line)[n] = '\0';
        }
    }

    return i;
}

static int getSection(const char *str, char **section, size_t n)
{
    const char *head = str;
    const char *tail = str + n;

    if (*head++ != '[')
        return 0;

    const char *p = strnchr(head, tail - head, ']');
    if (!p)
        return 0;

    int len = p - head;

    *section = malloc(len + 1);
    if (*section)
    {
        strncpy(*section, head, len);
        (*section)[len] = '\0';
        // printf("SECTION: %s\n", *section);
        return 1;
    }

    return 0;
}

static int getElement(const char *str, char **key, char **value, size_t n)
{
    int len;
    const char *head = str;
    const char *tail = str + n;
    const char *p = str;
    const char *p2;

    // Get token
    p = strnchr(head, tail - head, '=');
    if (!p)
        return 0;

    // Ignore comments #1
    p2 = strnchr(head, p - head, '#');
    if (p2)
    {
        // printf("IGNORE #1: %s\n", p2);
        return 0;
    }

    // Key
    len = p - head;
    *key = malloc(len + 1);
    if (!*key)
        goto FAILED;
    strncpy(*key, head, len);
    (*key)[len] = '\0';
    StringTrim(*key);
    // printf("KEY: %s\n", *key);

    head = p + 1;
    while (*head == ' ' || *head == '\t')
        head++;

    if (*head == '"')
    {
        head++;
        p = strnchr(head, tail - head, '"');
        if (!p)
            goto FAILED;
    }
    else
    {
        p = tail;
        // Ignore comments #2
        p2 = strnchr(head, tail - head, '#');
        if (p2)
        {
            // printf("IGNORE #2: %s\n", p2);
            p = p2;
        }
        while ((*(p - 1) == ' ' || *(p - 1) == '\t') && p > head)
            p--;
    }

    if (head >= p) // Null value
        goto FAILED;

    // value
    len = p - head;
    *value = malloc(len + 1);
    if (!*value)
        goto FAILED;
    strncpy(*value, head, len);
    (*value)[len] = '\0';
    // printf("VALUE: %s\n", *value);

    return 1;

FAILED:
    if (*key)
    {
        free(*key);
        *key = NULL;
    }
    if (*value)
    {
        free(*value);
        *value = NULL;
    }
    return 0;
}

int StringParseLine(const char *line, char **section, char **key, char **value)
{
    if (!line)
        return 0;

    const char *head = line;
    const char *tail = line + strlen(line);

    // Trim at beginning
    while (*head == ' ' || *head == '\t')
        head++;

    // Ignore comments #1
    if (*head == '#')
    {
        // printf("IGNORE: %s\n", head);
        return 0;
    }
    // printf("LINE: %s\n", line);

    if (section && getSection(head, section, tail - head))
        return 1;

    if (key && value && getElement(head, key, value, tail - head))
        return 1;

    return 0;
}

char *StringMakeShortByWidth(const char *str, int limit_w)
{
    if (!str)
        return NULL;

    int len = strlen(str);
    char *res = (char *)malloc(len + 4);
    if (!res)
        return NULL;
    strcpy(res, str);

    int cut_w = limit_w - GUI_GetTextWidth("...");
    int cut = 0;
    int max_w = 0;
    char ch;
    int i, count;
    for (i = 0; i < len; i += count)
    {
        if (res[i] == '\n')
        {
            res[i] = '\0';
            break;
        }

        count = GetUTF8Count(&res[i]);
        if (i + count > len) // 异常字符停止
        {
            res[i] = '\0';
            break;
        }

        ch = res[i + count];
        res[i + count] = '\0';
        max_w += GUI_GetTextWidth(&res[i]);
        res[i + count] = ch;

        if (max_w <= cut_w)
            cut = i + count;
        if (max_w > limit_w)
            break;
    }

    if (max_w > limit_w)
    {
        res[cut] = '\0';
        strcat(res, "...");
    }

    return res;
}

char *StringBreakLineByWidth(const char *str, int limit_w)
{
    if (!str)
        return NULL;

    int len = strlen(str);
    int new_len = len + 128; // 预留128个字符以添加\n
    char *res = malloc(new_len + 1);
    if (!res)
        return NULL;

    char *tmp;
    int w = 0, max_w = 0;
    int i, j, count;
    for (i = 0, j = 0; i < len; i += count, j += count)
    {
        if (str[i] == '\n')
        {
            res[j] = '\n';
            max_w = 0;
            count = 1;
            continue;
        }

        count = GetUTF8Count(&str[i]);
        if (i + count > len) // 异常字符停止
        {
            res[j] = '\0';
            break;
        }

        if (j + (count + 1) > new_len) // count+1 预留\n空间
        {
            new_len += 128;
            tmp = realloc(res, new_len + 1); // 扩展内存
            if (!tmp)
            {
                free(res);
                return NULL;
            }
            res = tmp;
        }

        strncpy(&res[j], &str[i], count);
        res[j + count] = '\0';
        w = GUI_GetTextWidth(&res[j]);

        if (max_w + w > limit_w)
        {
            res[j++] = '\n'; // 添加换行符并前移一字节
            strncpy(&res[j], &str[i], count);
            res[j + count] = '\0';
            max_w = w;
        }
        else
        {
            max_w += w;
        }
    }

    if (j < new_len)
    {
        tmp = realloc(res, j + 1); // 缩减内存
        if (tmp)
            res = tmp;
    }
    res[j] = '\0';

    return res;
}

int StringToListByWidthFromBuffer(LinkedList *list, char *buffer, size_t size, int limit_w)
{
    if (!list || !buffer)
        return 0;

    char *head = buffer;
    char *tail = buffer + size;
    char *cut = buffer;
    char *cur = buffer;
    char *sep = buffer;
    int line_w = 0;
    int max_w = 0;
    int cut_w = 0;
    int w;
    int count;
    char ch;

    while (cur < tail)
    {
        if (*cur == '\n')
        {
            *cur = '\0';
            StringListAdd(list, head);
            *cur = '\n';

            if (line_w > max_w)
                max_w = line_w;
            line_w = cut_w = 0;
            head = cut = ++cur;
            continue;
        }

        count = GetUTF8Count(cur);
        if (cur + count > tail) // 异常字符停止
        {
            ch = *cur;
            *cur = '\0';
            StringListAdd(list, head);
            *cur = ch;
            break;
        }

        ch = *(cur + count);
        *(cur + count) = '\0';
        w = GUI_GetTextWidth(cur);
        *(cur + count) = ch;

        if (line_w + w > limit_w)
        {
            // 判断是否当前为英文字符被截断
            if (cur > head && IS_ENGLISH_CHARACTER(*cur) && IS_ENGLISH_CHARACTER(*(cur - 1)))
            {
                sep = cut; // 回退到可截断的位置
            }
            else
            {
                sep = cur;
                cut_w = line_w;
            }

            if (cut_w > max_w)
                max_w = cut_w;

            ch = *sep;
            *sep = '\0';
            StringListAdd(list, head);
            *sep = ch;

            line_w -= cut_w;
            cut_w = 0;
            head = cut = sep;
        }

        if (*cur == ' ' || *cur == '\t' || count > 1) // 记录最新的可回退位置，空格、制表符或大于1字节的字符
        {
            cut = cur + count;
            cut_w = line_w + w;
        }

        line_w += w;
        cur += count;
    }

    if (head < tail)
        StringListAdd(list, head);

    if (line_w > max_w)
        max_w = line_w;

    return max_w;
}

int StringToListByWidth(LinkedList *list, const char *str, int limit_w)
{
    if (!list || !str)
        return 0;

    int len = strlen(str);
    char *buf = malloc(len + 1);
    if (!buf)
        return 0;
    strcpy(buf, str);

    int ret = StringToListByWidthFromBuffer(list, buf, len, limit_w);
    free(buf);

    return ret;
}
